package com.jrx.anytxn.customer.service.impl;

import com.google.common.collect.Lists;
import com.jrx.anytxn.common.constant.Constants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.JsonUtils;
import com.jrx.anytxn.common.uitl.SeqUtils;
import com.jrx.anytxn.customer.bean.CreditApprovalBean;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.constant.ErrorMsgConstant;
import com.jrx.anytxn.customer.dto.creditinfo.AdjustLimitRecordRes;
import com.jrx.anytxn.customer.dto.creditinfo.CreditActiveReq;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfo;
import com.jrx.anytxn.customer.dto.creditinfo.CreditInfoTree;
import com.jrx.anytxn.customer.dto.customer.CustomerLimitInfoRes;
import com.jrx.anytxn.customer.dto.customer.CustomerRenewalReq;
import com.jrx.anytxn.customer.dto.customer.CustomerRenewalRes;
import com.jrx.anytxn.customer.entity.CmCustomerFirstLevel;
import com.jrx.anytxn.customer.entity.CmCustomerLimitInfo;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.entity.CmLimitAdjustLog;
import com.jrx.anytxn.customer.entity.CmLimitChangeLog;
import com.jrx.anytxn.customer.entity.TlMarketingRecord;
import com.jrx.anytxn.customer.enums.CustomerMaintenanceType;
import com.jrx.anytxn.customer.mapper.base.CmLimitAdjustLogMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerFirstLevelMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerLimitInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerSecondLevelMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmLimitAdjustLogMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmLimitChangeLogMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtTlMarketingRecordMapper;
import com.jrx.anytxn.customer.plugins.HistoryLog;
import com.jrx.anytxn.customer.plugins.HistoryLogHelper;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.feign.api.request.ProductActiveReq;
import com.jrx.anytxn.feign.api.response.ProductActiveDto;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.feign.gateway.bean.ApiResponse;
import com.jrx.anytxn.feign.transaction.request.EtlSettleReq;
import com.jrx.anytxn.feign.transaction.response.EtlSettleRes;
import com.jrx.anytxn.feign.transaction.service.IAnyTxnTransactionService;
import com.jrx.anytxn.i18n.response.TxnRespResult;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.entity.PrCreditLimitMapping;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.mapper.ext.ExtPrCreditLimitMappingMapper;
import com.jrx.anytxn.param.service.limit.ICreditLimitMappingService;
import com.jrx.anytxn.param.service.limit.ICreditLimitTypeService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * 客户额度管理的实现
 *
 * @author Qingyao Xing
 * @date 2019/7/18 17:06
 */
@Service
public class CustomerLimitInfoServiceImpl implements ICustomerLimitInfoService {

    private static Logger logger = LoggerFactory.getLogger(CustomerLimitInfoServiceImpl.class);

    @Resource
    private ExtCmCustomerLimitInfoMapper extCmCustomerLimitInfoMapper;
    @Resource
    private ICreditLimitMappingService iCreditLimitMappingService;
    @Resource
    private ICreditLimitTypeService iCreditLimitTypeService;
    @Resource
    private ExtPrCreditLimitMappingMapper extPrCreditLimitMappingMapper;
    @Resource
    private CmLimitAdjustLogMapper cmLimitAdjustLogMapper;
    @Resource
    private ExtCmLimitAdjustLogMapper extCmLimitAdjustLogMapper;
    @Autowired
    private ISegmentService iSegmentService;
    @Autowired
    private IOrganizationTableService organizationTableService;
    @Resource
    private ExtTlMarketingRecordMapper extTlMarketingRecordMapper;
    @Resource
    private ExtCmLimitChangeLogMapper extCmLimitChangeLogMapper;
    @Resource
    private ExtCmCustomerFirstLevelMapper extCmCustomerFirstLevelMapper;
    @Resource
    private ExtCmCustomerSecondLevelMapper extCmCustomerSecondLevelMapper;
    @Resource
    private IAnyTxnTransactionService iAnyTxnTransactionService;
    @Resource
    private ICustomerLimitInfoService iCustomerLimitInfoService;
    @Resource
    private IUserApiService iUserApiService;
    @Resource
    private ICustomerService iCustomerService;


    @Override
    public CmCustomerLimitInfo getCustomerLimitInfo(String customerId, String organizationId, String channel, String productId, String contractNumber, BigDecimal loanAmount, String tenantId) throws TxnBizException {
        if (StringUtils.isBlank(customerId) && StringUtils.isBlank(organizationId) && StringUtils.isBlank(channel) && StringUtils.isBlank(productId)) {
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
        /*获取*/
        /*根据organization_id+channel+product_id+status在额度节点映射表获取额度节点ID*/
        String limitId = iCreditLimitMappingService.getLimitId(organizationId, channel, productId, tenantId);
        if (StringUtils.isBlank(limitId)) {
            logger.error("获取客户额度信息时额度节点为空");
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
        /*根据额度节点，机构号和租户ID在额度节点表获取额度属性*/
        String userModal = iCreditLimitTypeService.getUserModal(limitId, organizationId, tenantId);
        logger.debug("获取客户额度信息");
        CmCustomerLimitInfo cmCustomerLimitInfo;
        //根据额度节点属性的取值决定合同号的取值，即为use_mode为0循环，则合同号为“”空字符串，否则为接口上送的合同号
        if (StringUtils.equals(userModal, CustomerConstant.USER_MODEL_0)) {
            cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByChannel(customerId, organizationId, limitId, null, tenantId, channel);
        } else {
            if (StringUtils.isBlank(contractNumber)) {
                logger.error("额度节点属性为非循环时，接口传送的合同号为空");
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
            cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByChannel(customerId, organizationId, limitId, contractNumber, tenantId, channel);
        }
        /*客户不存在*/
        if (cmCustomerLimitInfo == null) {
            logger.error("查询的客户额度信息不存在");
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        return cmCustomerLimitInfo;
    }

    @Override
    public void verifyCustomer(CmCustomerLimitInfo info, BigDecimal loanAmount, Date businessDate) throws TxnBizException {
        while (info != null) {
            if (info.getLimitStatus().equals(CustomerConstant.STATUS_FREEZE)) {
                logger.error("客户额度状态为冻结状态");
                throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_2);
            }
            if (info.getLimitStatus().equals(CustomerConstant.STATUS_FAILURE)) {
                logger.error("客户额度状态为失效状态");
                throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_0);
            }
            if (info.getLimitStatus().equals(CustomerConstant.STATUS_NOT_ACTIVE)) {
                logger.error("客户额度状态为失效状态");
                throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_3);
            }
            if (info.getLimitStatus().equals(CustomerConstant.STATUS_CANCEL)) {
                logger.error("客户额度状态为注销状态");
                throw new TxnBizException(ErrorMsgConstant.CUSTOMER_LIMIT_STATUS_4);
            }
            //此额度节点的生效时间与失效时间进行比对
            if (info.getLimitExpiryDate() != null && info.getLimitEffectiveDate() != null) {
                int startFlag = DateUtils.compare(info.getLimitEffectiveDate(), businessDate);
                int endFlag = DateUtils.compare(info.getLimitExpiryDate(), businessDate);
                if (startFlag > 0 || endFlag < 0) {
                    String dateError = String.format(ErrorMsgConstant.CUSTOMER_CREDIT_ERROE_DATE, businessDate, info.getLimitId(), info.getLimitEffectiveDate(), info.getLimitExpiryDate());
                    logger.error(dateError);
                    throw new TxnBizException(dateError);
                }
            }
            //判断额度节点检核标识需是否是 1 检查
            if (StringUtils.equals(info.getAuthCheck(), CustomerConstant.AUTH_CHECK_1)) {//需要检查
                //判断贷款额度是否大于可用额度
                if (loanAmount.compareTo(info.getLimitAvailable()) > 0) {
                    //错误信息
                    String errMsg = String.format(ErrorMsgConstant.NO_CUSLIMIT, info.getLimitId(), info.getLimitAvailable().toString(), loanAmount.toString());
                    //可用额度不够抛出异常
                    logger.warn(errMsg);
                    throw new TxnBizException(errMsg);
                }
            }
            if (StringUtils.isNotBlank(info.getFatherId())) {
                //获取当前节点的父节点客户额度信息
                info = extCmCustomerLimitInfoMapper.selectByTenIdAndCusIdAndLimitId(info.getTenantId(), info.getCustomerId(), info.getFatherId());
            } else {
                break;
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyCustomerLimit(CmCustomerLimitInfo cmCustomerLimitInfo, BigDecimal loanAmount, String userId) throws TxnBizException {
        String fatherId;
        logger.debug("当客户额度信息存在情况下，修改客户已用额度和可用额度");
        while (cmCustomerLimitInfo != null) {
            fatherId = cmCustomerLimitInfo.getFatherId();
            BigDecimal limitUsed = cmCustomerLimitInfo.getLimitUsed().add(loanAmount);
            BigDecimal limitAvailable = cmCustomerLimitInfo.getLimitAmount().subtract(limitUsed);
            if(limitAvailable.compareTo(BigDecimal.ZERO)<0){
                limitAvailable = BigDecimal.ZERO;
            }
            cmCustomerLimitInfo.setLimitAvailable(limitAvailable);
            cmCustomerLimitInfo.setLimitUsed(limitUsed);
            cmCustomerLimitInfo.setUpdateBy(userId);
            int a = extCmCustomerLimitInfoMapper.updateCustomerLimit(cmCustomerLimitInfo);
            if (a < 1) {
                logger.error("修改客户额度信息失败");
                throw new TxnBizException(ErrorMsgConstant.ERROR_UPDATE);
            }
            if (fatherId != null) {
                cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByTenIdAndCusIdAndLimitId(cmCustomerLimitInfo.getTenantId(), cmCustomerLimitInfo.getCustomerId(), fatherId);
            } else {
                cmCustomerLimitInfo = null;
            }
        }
    }


    /**
     * 获取客户在对应渠道下的授信额度集合
     * @param organizationId 机构号
     * @param customerId     客户id
     * @param tenantId       租户id
     */
    @Override
    public List<CmCustomerLimitInfo> selectCusLimitListByCustId(String organizationId, String customerId, String tenantId) {

        return extCmCustomerLimitInfoMapper.selectCusLimitListByCusId(organizationId, customerId, tenantId);

    }


    /**
     * 授信管理
     *
     * @param bean 授信请求数据逻辑处理后的map集合
     * @throws TxnException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registerCustomerLimitInfo(CreditApprovalBean bean) throws Exception {
        //获取bean中得添加集合
        List<CmCustomerLimitInfo> addList = bean.getAddCmCustomerLimitInfos();
        //遍历添加集合
        for (CmCustomerLimitInfo customerLimitInfo : addList) {
            //客户添加额度
            int addSize = extCmCustomerLimitInfoMapper.insertSelective(customerLimitInfo);
            if (addSize <= 0) {
                //添加失败 抛异常
                logger.error(ErrorMsgConstant.CREDIT_LIMIT_SAVE_ERROR);
                throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
            }
            //添加记录到 调额历史表 只记录子节点的额度信息
            buildCmLimitAdjustLog(CustomerConstant.SUCCESS,"授信",CustomerConstant.ADJUST_TYPE_3,customerLimitInfo,null);
            //添加记录到 产品额度变更记录（续期供数使用）表
            if (StringUtils.isNotBlank(customerLimitInfo.getContractNumber())
                    && Objects.nonNull(customerLimitInfo.getLimitEffectiveDate())
                    && Objects.nonNull(customerLimitInfo.getLimitExpiryDate())) {
                buildCmLimitChangeLog(customerLimitInfo);
            }
        }
        //获取bean中的修改集合
        List<CmCustomerLimitInfo> modifyList = bean.getModifyCmCustomerLimitInfos();

        if (!modifyList.isEmpty()) {
            //获取集合中第一个
            CmCustomerLimitInfo info = modifyList.get(0);
            //循环修改集合
            for (CmCustomerLimitInfo cmCustomerLimitInfo : modifyList) {
                //修改客户额度
                int i = extCmCustomerLimitInfoMapper.updateCustomerLimitNewPerm(cmCustomerLimitInfo);
                if (i <= 0) {
                    //修改失败 抛异常
                    logger.error(ErrorMsgConstant.CREDIT_LIMIT_MODIFY_ERROR);
                    throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
                }
            }
        }

        //获取营销信息
        TlMarketingRecord marketingRecord = bean.getMarketingRecord();
        if (null != marketingRecord) {
            int marketAdd = extTlMarketingRecordMapper.insertSelective(marketingRecord);
            if (marketAdd <= 0) {
                throw new TxnBizException(ErrorMsgConstant.MARKET_ADD_ERROR);
            }
        }
    }

    /**
     * 插入或者更新产品额度变更记录（续期供数使用）表
     * @param limitInfo
     * @throws Exception
     */
    private void buildCmLimitChangeLog(CmCustomerLimitInfo limitInfo) throws TxnBizException {

        String organizationId = limitInfo.getOrganizationId();
        String tenantId = limitInfo.getTenantId();
        String customerId = limitInfo.getCustomerId();
        String channel = limitInfo.getChannel();
        Date limitExpiryDate = limitInfo.getLimitExpiryDate();
        String limitId = limitInfo.getLimitId();
        String contractNumber = limitInfo.getContractNumber();

        PrOrganizationTable organization = organizationTableService.findByOrId(organizationId, tenantId);
        Date today = organization.getNextProcessingDate();
        List<PrCreditLimitMapping> prCreditLimitMappings = extPrCreditLimitMappingMapper.selectLimitListIdAndTenantId(organizationId, channel, tenantId, limitId);
        String productId = prCreditLimitMappings.get(0).getProductId();
        // 通过产品编号判断同系列有哪些产品编号
        List<String> seriesProducts = BizTypeProducts.getSeriesProductIds(productId);
        //用户是否已经有对应的综合额度信息
        CmLimitChangeLog changeLog = extCmLimitChangeLogMapper.queryFirstLimitChangeLog(tenantId, customerId, seriesProducts);
        // 存在-则需要执行修改操作
        if (Objects.nonNull(changeLog)) {
            Date etlExpiryDate = changeLog.getEtlExpiryDate();
            String etlContractNumber = changeLog.getEtlContractNumber();
            String etlLimitId = changeLog.getEtlLimitId();
            Date date = limitExpiryDate.compareTo(etlExpiryDate) > 0 ? limitExpiryDate : etlExpiryDate;
            // 已过期-直接新增
            if (etlExpiryDate.compareTo(today) < 0) {
                logger.info("已过期，是重新授信的数据");
                addLimitChangeLog(limitInfo, productId);
            } else {
                logger.info("未过期-需要新增数据，并且更新旧有的数据");
                //未过期-需要新增数据，并且更新旧有的数据
                CmLimitChangeLog updateLimitChangeLog = new CmLimitChangeLog();
                updateLimitChangeLog.setTenantId(tenantId);
                updateLimitChangeLog.setEtlExpiryDate(date);
                updateLimitChangeLog.setCustomerId(customerId);
                updateLimitChangeLog.setEtlContractNumber(etlContractNumber);
                updateLimitChangeLog.setEtlLimitId(etlLimitId);
                // 本产品的旧数据不需要更新，通过原授信合同号过滤，然后新增一条数据进去
                updateLimitChangeLog.setContractNumber(limitInfo.getContractNumber());
                extCmLimitChangeLogMapper.updateEtlExpiryDate(updateLimitChangeLog);

                CmLimitChangeLog addLimitChangeLog = new CmLimitChangeLog();
                BeanUtils.copyProperties(changeLog, addLimitChangeLog);
                try {
                    addLimitChangeLog.setId(iSegmentService.getId("CmLimitChangeLog"));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                addLimitChangeLog.setProductId(productId);
                addLimitChangeLog.setContractNumber(contractNumber);
                addLimitChangeLog.setExpiryDate(limitExpiryDate);
                addLimitChangeLog.setEtlExpiryDate(date);
                addLimitChangeLog.setRecordVersionNumber(1);
                addLimitChangeLog.setCreateTime(new Date());
                addLimitChangeLog.setUpdateTime(new Date());
                //新增一条记录
                extCmLimitChangeLogMapper.insertSelective(addLimitChangeLog);
            }
        } else {
            logger.info("首次重新授信的数据");
            //不存在执行新增操作
            addLimitChangeLog(limitInfo, productId);
        }

    }

    /**
     * 新增产品额度变更记录
     * @param limitInfo
     * @param productId
     * @throws SegmentException
     */
    public void addLimitChangeLog(CmCustomerLimitInfo limitInfo, String productId) {
        String tenantId = limitInfo.getTenantId();
        String customerId = limitInfo.getCustomerId();
        String limitId = limitInfo.getLimitId();
        String contractNumber = limitInfo.getContractNumber();
        String channel = limitInfo.getChannel();
        Date limitExpiryDate = limitInfo.getLimitExpiryDate();
        Date limitEffectiveDate = limitInfo.getLimitEffectiveDate();
        CmLimitChangeLog addLimitChangeLog = new CmLimitChangeLog();
        try {
            addLimitChangeLog.setId(iSegmentService.getId("CmLimitChangeLog"));
        } catch (SegmentException e) {
            throw new RuntimeException(e);
        }
        addLimitChangeLog.setTenantId(tenantId);
        addLimitChangeLog.setChannel(channel);
        addLimitChangeLog.setProductId(productId);
        addLimitChangeLog.setCustomerId(customerId);
        addLimitChangeLog.setContractNumber(contractNumber);
        addLimitChangeLog.setEtlContractNumber(contractNumber);
        addLimitChangeLog.setEtlLimitId(limitId);
        addLimitChangeLog.setEffectiveDate(limitEffectiveDate);
        addLimitChangeLog.setExpiryDate(limitExpiryDate);
        addLimitChangeLog.setEtlEffectiveDate(limitEffectiveDate);
        addLimitChangeLog.setEtlExpiryDate(limitExpiryDate);
        addLimitChangeLog.setRecordVersionNumber(1);
        extCmLimitChangeLogMapper.insertSelective(addLimitChangeLog);

    }


    @Override
    public ArrayList<AdjustLimitRecordRes> getAdjustLimitRecord(String tenantId, String customerId,String channel) {
        ArrayList<AdjustLimitRecordRes> adjustLimitRecordResList = new ArrayList<AdjustLimitRecordRes>();
        //获取调额历史表
        List<CmLimitAdjustLog> cmLimitAdjustLogList = extCmLimitAdjustLogMapper.selectByTenIdAndCusIdAndChannel(tenantId, customerId,channel);
        if (cmLimitAdjustLogList.isEmpty()) {
            return null;
        }
        for (CmLimitAdjustLog cmLimitAdjustLog : cmLimitAdjustLogList) {
            AdjustLimitRecordRes adjustLimitRecordRes = new AdjustLimitRecordRes();
            //获取客户额度信息
            CmCustomerLimitInfo cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByTenIdAndCusIdAndLimitId(tenantId, customerId, cmLimitAdjustLog.getLimitId());
            adjustLimitRecordRes.setAuthCheck(cmCustomerLimitInfo.getAuthCheck());
            adjustLimitRecordRes.setDateTime(cmLimitAdjustLog.getUpdateTime());
            adjustLimitRecordRes.setLimitDesc(cmCustomerLimitInfo.getChineseName());
            //adjustLimitRecordRes.setLimitPermAfter(cmCustomerLimitInfo.getLimitPerm());
            adjustLimitRecordRes.setLimitPermAfter(cmLimitAdjustLog.getLimitPermAfter());
            adjustLimitRecordRes.setLimitTempEndDateAfter(cmCustomerLimitInfo.getLimitExpiryDate());//额度失效日期
            adjustLimitRecordRes.setLimitTempStartDateAfter(cmCustomerLimitInfo.getLimitEffectiveDate());//额度生效日期
            adjustLimitRecordRes.setLimitType(cmCustomerLimitInfo.getLimitClass());
            adjustLimitRecordRes.setUseMode(cmCustomerLimitInfo.getUseMode());
            adjustLimitRecordRes.setUpdateBy(cmLimitAdjustLog.getUpdateBy());
            //调整类型
            adjustLimitRecordRes.setAdjustType(cmLimitAdjustLog.getAdjustType());
            //调整原因
            adjustLimitRecordRes.setAdjustReason(cmLimitAdjustLog.getAdjustReason());
            adjustLimitRecordResList.add(adjustLimitRecordRes);
        }
        return adjustLimitRecordResList;
    }

    /**
     * 获取对应状态，渠道，客户的节点集合的客户额度授信集合
     *
     * @param limitIds       额度节点集合
     * @param organizationId 机构号
     * @param channel        渠道
     * @param customerId     客户id
     * @param tenantId       租户id
     * @param status         状态
     */
    @Override
    public List<CmCustomerLimitInfo> getCusLimitByLimtIdAndCusIdAndOrgId(List<String> limitIds, String organizationId, String channel, String customerId, String tenantId, String status) throws TxnException {

        List<CmCustomerLimitInfo> list = extCmCustomerLimitInfoMapper.selectCusLimitByPrdAndCusIdAndOrgId(limitIds, organizationId, channel, customerId, tenantId, status);
        if (list.isEmpty()) {
            logger.error(ErrorMsgConstant.CUSTOMER_QUERY_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        return list;
    }

    /**
     * 客户额度调整
     *
     * @param customerLimitInfoList 客户额度实体集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyCustomerLimitList(List<CmCustomerLimitInfo> customerLimitInfoList, List<CmLimitAdjustLog> limitAdjustLogList) throws TxnException {
        if (!customerLimitInfoList.isEmpty()) {
            for (CmCustomerLimitInfo cmCustomerLimitInfo : customerLimitInfoList) {
                int i = extCmCustomerLimitInfoMapper.updateCustomerLimitAdjust(cmCustomerLimitInfo);
                if (i <= 0) {
                    logger.error(ErrorMsgConstant.CREDIT_LIMIT_MODIFY_ERROR);
                    throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
                }
                //添加记录到 产品额度变更记录（续期供数使用）表
                if (StringUtils.isNotBlank(cmCustomerLimitInfo.getContractNumber())
                        && Objects.nonNull(cmCustomerLimitInfo.getLimitEffectiveDate())
                        && Objects.nonNull(cmCustomerLimitInfo.getLimitExpiryDate())) {
                    buildCmLimitChangeLog(cmCustomerLimitInfo);
                }
            }
            for (CmLimitAdjustLog cmLimitAdjustLog : limitAdjustLogList) {
                cmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
            }
        }
    }

    /**
     * 获取客户额度的额度树
     *
     * @param organizationId 机构号
     * @param customerId     客户id
     * @param status         状态
     * @param tenantId       租户id
     */
    @Override
    public List<CreditInfoTree> getCusLimitTree(String organizationId, String customerId, String status, String tenantId) throws TxnException {

        List<CmCustomerLimitInfo> list = extCmCustomerLimitInfoMapper.selectCusLimitByCusIdAndOrgIdAndStatus(organizationId, customerId, status, tenantId);

        if (list.isEmpty()) {
            logger.info(ErrorMsgConstant.CUSTOMER_TREE_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        //获取额度节点与描述的map集合
        Map<String, List<CmCustomerLimitInfo>> customerLimitInfoMap = list.stream().collect(Collectors.groupingBy(CmCustomerLimitInfo::getLimitId));

        List<CreditInfoTree> creditInfoTreeList = list.stream().map(cmCustomerLimitInfo -> {
            CreditInfoTree creditInfoTree = new CreditInfoTree();
            //额度节点编号放入
            creditInfoTree.setLimitId(cmCustomerLimitInfo.getLimitId());
            //额度节点描述 放入
            creditInfoTree.setLimitDesc(cmCustomerLimitInfo.getChineseName());
            //授信额度
            creditInfoTree.setCreditLimit(cmCustomerLimitInfo.getLimitAmount());
            //当前可用额度
            creditInfoTree.setAvailCreditLimit(cmCustomerLimitInfo.getLimitAvailable());
            //额度生效日期
            creditInfoTree.setEffectiveDate(cmCustomerLimitInfo.getLimitEffectiveDate());
            //额度失效日期
            creditInfoTree.setExpiryDate(cmCustomerLimitInfo.getLimitExpiryDate());
            //额度状态
            creditInfoTree.setLimitStatus(cmCustomerLimitInfo.getLimitStatus());
            //父节点编号
            String fatherId = cmCustomerLimitInfo.getFatherId();

            creditInfoTree.setFatherId(fatherId);
            //父节点描述
            if (StringUtils.isBlank(fatherId)) {
                creditInfoTree.setFatherDesc("");
            } else {
                List<CmCustomerLimitInfo> cmCustomerLimitInfos = customerLimitInfoMap.get(fatherId);
                if (cmCustomerLimitInfos == null || cmCustomerLimitInfos.isEmpty()) {
                    creditInfoTree.setFatherDesc("");
                } else {
                    creditInfoTree.setFatherDesc(cmCustomerLimitInfos.get(0).getChineseName());
                }
            }
            return creditInfoTree;
        }).collect(Collectors.toList());

        return creditInfoTreeList;
    }

    /**
     * 额度冻结/解冻
     *
     * @param cmCustomerLimitInfoList 客户额度实体集合
     * @throws TxnException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyCustomerLimitStatus(List<CmCustomerLimitInfo> cmCustomerLimitInfoList) throws TxnException {
        try {
            if (!cmCustomerLimitInfoList.isEmpty()) {
                CmCustomerLimitInfo info = cmCustomerLimitInfoList.get(0);
                HistoryLog txnHistoryLog = new HistoryLog(info.getCustomerId(), info.getTenantId(), info.getOrganizationId(), info.getChannel(), info.getUpdateBy(), CustomerMaintenanceType.CREDIT_FREEZE_RELEASE.getCode());
                HistoryLogHelper.setLocalHistory(txnHistoryLog);
                for (CmCustomerLimitInfo customerLimitInfo : cmCustomerLimitInfoList) {
                    int i = extCmCustomerLimitInfoMapper.updateCustomerLimitStatus(customerLimitInfo);
                    if (i <= 0) {
                        logger.info(ErrorMsgConstant.CREDIT_LIMIT_MODIFY_STATUS_ERROR);
                        throw new TxnException(TxnRespCode.BIZ_ERROR, TxnRespCode.BIZ_ERROR.getUserTips());
                    }
                }
            }
        } finally {
            HistoryLogHelper.clearLocalHistory();
        }
    }

    /**
     * 客户额度列表分页查询
     *
     * @param customerId 客户id
     * @param tenantId   租户id
     */
    @Override
    public ArrayList<CustomerLimitInfoRes> getLimitList(String customerId, String tenantId) {
        CmCustomerLimitInfo cond = new CmCustomerLimitInfo();
        cond.setCustomerId(customerId);
        cond.setTenantId(tenantId);
        List<CmCustomerLimitInfo> ls = extCmCustomerLimitInfoMapper.selectByCondition(cond);
        List<CustomerLimitInfoRes> res = BeanMapping.copyList(ls, CustomerLimitInfoRes.class);
        return (ArrayList<CustomerLimitInfoRes>) res;
    }

    /**
     * 还款修改客户额度
     *
     * @param tenantId       租户id
     * @param customerId     客户id
     * @param organizationId 机构号
     * @param channel        渠道
     * @param productId      产品id
     * @param payAmount      还款金额
     * @throws TxnBizException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyCustomerLimitByPayment(String tenantId, String customerId, String organizationId, String channel, String productId, BigDecimal payAmount) throws TxnBizException {
        if(payAmount.compareTo(BigDecimal.ZERO)<=0){
            return;
        }
        /*根据机构号，渠道，产品ID和租户ID字段在额度节点映射表获取额度节点ID*/
        String limitId = iCreditLimitMappingService.getLimitId(organizationId, channel, productId, tenantId);
        if (StringUtils.isBlank(limitId)) {
            logger.error("获取客户额度信息时额度节点为空");
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
        /*根据额度节点，机构号和租户ID在额度节点表获取额度属性*/
        String userModal = iCreditLimitTypeService.getUserModal(limitId, organizationId, tenantId);
        //根据额度节点属性的取值决定合同号的取值，即为use_mode为0循环，则合同号为“”空字符串，否则为接口上送的合同号
        if (StringUtils.equals(userModal, CustomerConstant.USER_MODEL_0)) {
            this.modifyCustomerLimit(tenantId,customerId, organizationId, channel, productId, payAmount,null);
        } else {
            logger.info(String.format("租户[%s]客户[%s]额度节点[%s]属性为非循环时，客户额度不能修改", tenantId, customerId, limitId));
        }
    }

    /**
     * 恢复客户额度（所有额度类型都恢复）
     *
     * @param tenantId       租户id
     * @param customerId     客户id
     * @param organizationId 机构号
     * @param channel        渠道
     * @param productId      产品id
     * @param amount         金额
     * @throws TxnBizException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyCustomerLimit(String tenantId, String customerId,String organizationId, String channel, String productId, BigDecimal amount, String contractNumber) throws TxnBizException {
        List<CmCustomerLimitInfo> limitInfos = new ArrayList<>();
        List<CmCustomerLimitInfo> customerLimitInfoList = extCmCustomerLimitInfoMapper.selectCusLimitListByCusId(organizationId,customerId,tenantId);

        /*根据机构号，渠道，产品ID和租户ID字段在额度节点映射表获取额度节点ID*/
        String limitId = iCreditLimitMappingService.getLimitId(organizationId, channel, productId, tenantId);
        if (StringUtils.isBlank(limitId)) {
            logger.error("获取客户额度信息时额度节点为空");
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
        //根据额度节点属性的取值决定合同号的取值，即为use_mode为0循环，则合同号为“”空字符串，否则为接口上送的合同号
        CmCustomerLimitInfo info = null;
        String userModal = iCreditLimitTypeService.getUserModal(limitId, organizationId, tenantId);
        if(StringUtils.equals(userModal, CustomerConstant.USER_MODEL_0)) {
            logger.info(String.format("租户[%s]客户[%s]额度节点[%s]属性为循环时，客户额度变化[%s]", tenantId, customerId, limitId, amount));
            info = customerLimitInfoList.stream().filter(c -> c.getLimitId().equals(limitId))
                    .findFirst().orElse(null);
        }else{
            logger.info(String.format("租户[%s]客户[%s]外部合同号[%s]额度节点[%s]属性为非循环时，客户额度变化[%s]", tenantId, customerId, contractNumber,limitId, amount));
            info = customerLimitInfoList.stream().filter(c -> c.getLimitId().equals(limitId)).filter(c -> contractNumber.equals(c.getContractNumber()))
                    .findFirst().orElse(null);
        }

        if (Objects.isNull(info)) {
            logger.error("获取客户额度信息时为空");
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        //已用额度
        BigDecimal infoLimitUsed = info.getLimitUsed().subtract(amount);
        info.setLimitUsed(infoLimitUsed.compareTo(BigDecimal.ZERO) > 0 ? infoLimitUsed : BigDecimal.ZERO);

        //可用额度 = 总授信额度-已使用额度-实时还款未入账金额
        BigDecimal infoLimitAvailable = info.getLimitAmount().subtract(info.getLimitUsed()).subtract(info.getOnlinePaymentAmount());
        //BigDecimal infoLimitAvailable = info.getLimitAvailable().add(amount);
        info.setLimitAvailable(infoLimitAvailable.compareTo(BigDecimal.ZERO) > 0 ? infoLimitAvailable : BigDecimal.ZERO);

        //将节点信息放入修改集合
        limitInfos.add(info);


        //判断当前节点是否为null
        while (info != null) {
            //获取当前节点的父节点客户额度信息
            CmCustomerLimitInfo finalInfo = info;
            CmCustomerLimitInfo fatherInfo = customerLimitInfoList.stream().filter(c -> c.getLimitId().equals(finalInfo.getFatherId()))
                    .findFirst().orElse(null);
            if (null != fatherInfo) {
                //可用额度 恢复后可用额度不能大于总额度
                // 判断父节点额度计算方式，根据实际情况进行额度更
                //可用额度 恢复后可用额度不能大于总额度
                BigDecimal limitAvailable = fatherInfo.getLimitAvailable().add(amount);
                if(limitAvailable.compareTo(fatherInfo.getLimitAmount()) > 0){
                    limitAvailable = fatherInfo.getLimitAmount();
                }
                fatherInfo.setLimitAvailable(limitAvailable);//可用额度
                //已用额度
                BigDecimal limitUsed = fatherInfo.getLimitUsed().subtract(amount);
                if(limitUsed.compareTo(BigDecimal.ZERO) < 0){
                    limitUsed = BigDecimal.ZERO;
                }
                fatherInfo.setLimitUsed(limitUsed);
                //将父节点放入修改集合
                limitInfos.add(fatherInfo);
            }
            //将父节点信息放入当前节点循环
            info = fatherInfo;
        }
        //待修改客户集合不为空时执行修改功能
        if (!limitInfos.isEmpty()) {
            for (CmCustomerLimitInfo customerLimitInfo : limitInfos) {
                extCmCustomerLimitInfoMapper.updateCustomerLimitPayment(customerLimitInfo);
            }
        }

    }


    /**
     * 根据机构id,渠道号,客户编号,额度编号,租户号查询单条客户额度信息
     * @param organizationId
     * @param channel
     * @param customerId
     * @param limitId
     * @param tenantId
     * @return
     */
    @Override
    public CustomerLimitInfoRes selectCustomerLimitInfo(String organizationId, String channel, String customerId, String limitId, String tenantId) throws TxnException {

        CmCustomerLimitInfo cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectCustomerLimitInfo(customerId,organizationId,limitId,null,tenantId);
        if (cmCustomerLimitInfo==null) {
            logger.error(ErrorMsgConstant.CUSTOMER_QUERY_NODATA);
            throw new TxnException(TxnRespCode.NO_DATA, TxnRespCode.NO_DATA.getUserTips());
        }
        CustomerLimitInfoRes customerLimitInfoRes = BeanMapping.copy(cmCustomerLimitInfo,CustomerLimitInfoRes.class);
        return customerLimitInfoRes;
    }

    private void buildCmLimitAdjustLog(String result, String msg, String adjustType, CmCustomerLimitInfo customerLimitInfo,CmCustomerLimitInfo oldLimitInfo) throws TxnBizException {
        CmLimitAdjustLog cmLimitAdjustLog = new CmLimitAdjustLog();
        BeanUtils.copyProperties(customerLimitInfo,cmLimitAdjustLog);
        try {
            cmLimitAdjustLog.setId(iSegmentService.getId(Constant.CM_LIMIT_ADJUST_LOG));
        }catch (SegmentException e){
            logger.error("主键生成错误 ");
        }
        //获取业务日期
        PrOrganizationTable organization = organizationTableService.findByOrId(customerLimitInfo.getOrganizationId(),customerLimitInfo.getTenantId());
        cmLimitAdjustLog.setBusinessDate(organization.getNextProcessingDate());
        cmLimitAdjustLog.setSeqNo(SeqUtils.getSNByLength(32));//流水号
        cmLimitAdjustLog.setDataSource(CustomerConstant.DATASOURCE_2);
        cmLimitAdjustLog.setResult(result);
        cmLimitAdjustLog.setDescription(msg);
        cmLimitAdjustLog.setAdjustReason(msg);
        cmLimitAdjustLog.setAdjustType(adjustType);
        if(CustomerConstant.ADJUST_TYPE_0.equals(adjustType)){
            //永久额度
            cmLimitAdjustLog.setLimitPermBefore(oldLimitInfo.getLimitPerm());
            cmLimitAdjustLog.setLimitPermAfter(customerLimitInfo.getLimitPerm());
            //临时额度
            cmLimitAdjustLog.setLimitTempBefore(oldLimitInfo.getLimitTemp() == null ? BigDecimal.ZERO : oldLimitInfo.getLimitTemp());
            cmLimitAdjustLog.setLimitTempAfter(customerLimitInfo.getLimitTemp() == null ? BigDecimal.ZERO : customerLimitInfo.getLimitTemp());
            //临时额度生效日期
            cmLimitAdjustLog.setLimitTempStartDateBefore(oldLimitInfo.getLimitTempStartDate());
            cmLimitAdjustLog.setLimitTempEndDateBefore(oldLimitInfo.getLimitTempEndDate());

            cmLimitAdjustLog.setLimitTempStartDateAfter(customerLimitInfo.getLimitTempStartDate());
            cmLimitAdjustLog.setLimitTempEndDateAfter(customerLimitInfo.getLimitTempEndDate());
        }else{
            //永久额度
            cmLimitAdjustLog.setLimitPermBefore(customerLimitInfo.getLimitPerm());
            cmLimitAdjustLog.setLimitPermAfter(customerLimitInfo.getLimitPerm());
            //临时额度
            cmLimitAdjustLog.setLimitTempBefore(customerLimitInfo.getLimitTemp() == null ? BigDecimal.ZERO : customerLimitInfo.getLimitTemp());
            cmLimitAdjustLog.setLimitTempAfter(customerLimitInfo.getLimitTemp() == null ? BigDecimal.ZERO : customerLimitInfo.getLimitTemp());
            //临时额度生效日期
            cmLimitAdjustLog.setLimitTempStartDateBefore(customerLimitInfo.getLimitTempStartDate());
            cmLimitAdjustLog.setLimitTempEndDateBefore(customerLimitInfo.getLimitTempEndDate());

            cmLimitAdjustLog.setLimitTempStartDateAfter(customerLimitInfo.getLimitTempStartDate());
            cmLimitAdjustLog.setLimitTempEndDateAfter(customerLimitInfo.getLimitTempEndDate());
        }

        cmLimitAdjustLog.setUpdateBy(customerLimitInfo.getUpdateBy());
        cmLimitAdjustLog.setRecordVersionNumber(Constant.VERSION_NUMBER);

        extCmLimitAdjustLogMapper.insertSelective(cmLimitAdjustLog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelCreditModifyCustomerLimitInfo(CmCustomerLimitInfo cmCustomerLimitInfo) throws TxnException{
        try {
            List<CmCustomerLimitInfo> cmCustomerLimitInfoList = Lists.newArrayList();
            cmCustomerLimitInfo.setLimitStatus(CustomerConstant.STATUS_FAILURE);
            cmCustomerLimitInfoList.add(cmCustomerLimitInfo);
            // 通过租户id、客户id、节点状态,查询该用户的所有额度节点
            List<CmCustomerLimitInfo> customerLimitInfoList = extCmCustomerLimitInfoMapper.selectAllCustomerLimitInfo(cmCustomerLimitInfo.getTenantId(), cmCustomerLimitInfo.getCustomerId(), CustomerConstant.STATUS_NORMAL);
            // 授信取消失效金额
            BigDecimal creditCancelAmount = cmCustomerLimitInfo.getLimitAmount();
            // 计算后的额度金额
            BigDecimal amount;
            // 当前额度节点
            CmCustomerLimitInfo current = cmCustomerLimitInfo;
            // 父节点id如果不为空则去更新父节点额度
            while (StringUtils.isNotBlank(current.getFatherId())) {
                // 根据父节点id查询额度信息
                CmCustomerLimitInfo finalCurrent = current;
                cmCustomerLimitInfo = customerLimitInfoList.stream().filter(c -> c.getLimitId().equals(finalCurrent.getFatherId()))
                        .findFirst().orElse(null);
                if (cmCustomerLimitInfo == null) {
                    logger.info(">>>>取消授信<<<<更新客户额度时未查询到父节点信息,fatherId:{}", finalCurrent.getFatherId());
                    throw new TxnException("更新客户额度时未查询到父节点信息");
                }
                // 判断父节点额度计算方式，根据实际情况进行额度更新
                switch (cmCustomerLimitInfo.getOcFlag()) {
                    // 求和 父节点额度减去失效额度
                    case CustomerConstant.OC_FLAG_1:
                        amount = cmCustomerLimitInfo.getLimitAmount().subtract(creditCancelAmount);
                        break;
                    // 最大值
                    case CustomerConstant.OC_FLAG_2:
                        amount = calculationAmount(cmCustomerLimitInfo, current, customerLimitInfoList, CustomerConstant.OC_FLAG_2);
                        break;
                    // 最小值
                    case CustomerConstant.OC_FLAG_3:
                        amount = calculationAmount(cmCustomerLimitInfo, current, customerLimitInfoList, CustomerConstant.OC_FLAG_3);
                        break;
                    default:
                        amount = cmCustomerLimitInfo.getLimitAmount();
                }
                cmCustomerLimitInfo.setLimitAmount(amount);
                cmCustomerLimitInfo.setLimitAvailable(amount.subtract(cmCustomerLimitInfo.getLimitUsed()));
                cmCustomerLimitInfo.setLimitPerm(amount);
                cmCustomerLimitInfoList.add(cmCustomerLimitInfo);
                // 更新当前额度对象
                current = cmCustomerLimitInfo;
            }
            // 更新节点信息
            extCmCustomerLimitInfoMapper.batchUpdateCustomerLimitInfo(cmCustomerLimitInfoList);
        } catch (Exception e) {
            logger.info(">>>>取消授信<<<<更新客户额度异常", e);
            throw new TxnException("更新客户额度异常");
        }
    }

    private BigDecimal calculationAmount(CmCustomerLimitInfo father, CmCustomerLimitInfo child, List<CmCustomerLimitInfo> list, String ocFlag) {
        BigDecimal amount = BigDecimal.ZERO;
        // 查询该父节点下所有子节点，需要排除已经修改过的子节点，如果没有子节点，则为0
        List<CmCustomerLimitInfo> childLimitInfoList = list.stream()
                .filter(c -> father.getLimitId().equals(c.getFatherId()) && !c.getId().equals(child.getId()))
                .collect(Collectors.toList());
        // 上面过滤时排除了当前节点，因为该节点额度已经修改，如果该节点是正常状态，则加入列表
        if (CustomerConstant.STATUS_NORMAL.equals(child.getLimitStatus())) {
            childLimitInfoList.add(child);
        }
        if (childLimitInfoList.size() > 0) {
            amount = childLimitInfoList.get(0).getLimitAmount();
            for (CmCustomerLimitInfo c : childLimitInfoList) {
                // 取额度最大值
                if (CustomerConstant.OC_FLAG_2.equals(ocFlag) && amount.compareTo(c.getLimitAmount()) < 0) {
                    amount = c.getLimitAmount();
                }
                // 取额度最小值
                if (CustomerConstant.OC_FLAG_3.equals(ocFlag) && amount.compareTo(c.getLimitAmount()) > 0) {
                    amount = c.getLimitAmount();
                }
            }
        }
        return amount;
    }


    /**
     * 获取用户的授信信息-根据有效期排序（升序）
     * @param customerId
     * @param tenantId
     * @param organizationId
     * @param limitIds
     * @return
     */
    @Override
    public List<CmCustomerLimitInfo> getCmCustomerLimitInfos(String customerId, String tenantId, String organizationId, List<String> limitIds) {
        List<CmCustomerLimitInfo> list = extCmCustomerLimitInfoMapper.selectCustLimits(customerId, tenantId, organizationId,
                limitIds, Arrays.asList(CustomerConstant.STATUS_NORMAL, CustomerConstant.STATUS_FREEZE, CustomerConstant.STATUS_CANCEL));
        //取最早授信的授信额度信息
        List<CmCustomerLimitInfo> infosOrderBytLimitEffectiveDate = null;
        if (CollectionUtils.isNotEmpty(list)) {
            infosOrderBytLimitEffectiveDate = list.stream().filter(cmCustomerLimitInfo ->
                    Objects.nonNull(cmCustomerLimitInfo.getLimitEffectiveDate()))
                    .sorted(Comparator.comparing(CmCustomerLimitInfo::getLimitEffectiveDate))
                    .collect(Collectors.toList());
        }
        return infosOrderBytLimitEffectiveDate;
    }

    /**
     * 查询用户首次额度
     *
     * @param tenantId
     * @param customerId
     * @param limitIds
     * @param status
     * @param etlEffectiveDate
     * @return
     */
    @Override
    public CmLimitAdjustLog queryFirstAdjustLog(String tenantId, String customerId, List<String> limitIds, String status, Date etlEffectiveDate) {
        return extCmLimitAdjustLogMapper.queryFirstAdjustLimitInfo(tenantId, customerId, limitIds, status, etlEffectiveDate);
    }

    /**
     * 获取用户"首笔贷款额度信息"
     *
     * @param tenantId
     * @param customerId
     * @param productIds
     * @return
     */
    @Override
    public CmLimitChangeLog queryFirstLimitChangeLog(String tenantId, String customerId, List<String> productIds) {
        return extCmLimitChangeLogMapper.queryFirstLimitChangeLog(tenantId, customerId, productIds);
    }

    /**
     * 计算结清日期
     * @param tenantId
     * @param customerId
     * @param productId
     * @param organizationId
     * @param businessDate
     * @return
     * @throws TxnBizException
     */
    @Override
    public String calculateSettleDate(String tenantId, String customerId, String productId, String organizationId, Date businessDate) throws TxnBizException {
        List<String> seriesProductIds = BizTypeProducts.getSeriesProductIds(productId);
        // 获取用户"首笔贷款额度信息"
        CmLimitChangeLog firstLimitInfo = this.queryFirstLimitChangeLog(tenantId, customerId, seriesProductIds);
        String settleDate = null;
        if (Objects.isNull(firstLimitInfo)) {
            return null;
        }
        //获取客户相关的所有额度编号，做集合运算  兴安花系列产品对应的额度列表
        List<String> limitIds = extPrCreditLimitMappingMapper.selectLimitIdByTenIdAndProductIds(tenantId, seriesProductIds);
        if (limitIds.isEmpty()) {
            return null;
        }
        List<CmCustomerLimitInfo> cusLimits = iCustomerLimitInfoService.getCmCustomerLimitInfos(customerId, tenantId, organizationId, limitIds);
        if (CollectionUtils.isEmpty(cusLimits)) {
            return null;
        }
        // 实际上的最大的额度有期限
        Date maxExpireDate = cusLimits.stream().map(CmCustomerLimitInfo::getLimitExpiryDate).max(Date::compareTo).orElse(businessDate);
        if (Objects.nonNull(firstLimitInfo.getEtlSettleDate())) {
            settleDate = DateUtils.format(firstLimitInfo.getEtlSettleDate());
        } else if (maxExpireDate.compareTo(businessDate) <= 0) {
            // 额度失效-计算结清日期
            List<CmCustomerLimitInfo> cmCustomerLimitInfoList = extCmCustomerLimitInfoMapper.selectCustLimits(customerId, tenantId, organizationId, limitIds, null);
            // 已使用额度是否为0
            boolean present = cmCustomerLimitInfoList.stream().anyMatch(limit -> limit.getLimitUsed().compareTo(BigDecimal.ZERO) > 0);
            BigDecimal totalAmount = BigDecimal.ZERO;
            TxnRespResult<BigDecimal> result = iAnyTxnTransactionService.getLoanTotalAmount(firstLimitInfo.getChannel(), customerId, tenantId, productId);
            // 没有已使用额度，表示已经结清了
            if (null == result || 1000 != result.getCode()) {
                logger.error("客户->{}查询总欠款失败", customerId);
                return settleDate;
            } else {
                totalAmount = totalAmount.add(result.getData());
            }
            if (!present && BigDecimal.ZERO.compareTo(totalAmount) == 0) {
                //获取结清日期
                EtlSettleReq etlSettleReq = new EtlSettleReq();
                etlSettleReq.setTenantId(tenantId);
                etlSettleReq.setCustomerId(customerId);
                etlSettleReq.setProducts(seriesProductIds);
                TxnRespResult<EtlSettleRes> respResult = iAnyTxnTransactionService.getEtlSettleDate(etlSettleReq);
                if (null == respResult || 1000 != respResult.getCode()) {
                    logger.error("客户->{}获取结清日期失败", customerId);
                } else {
                    Date etlSettleDate = respResult.getData().getEtlSettleDate();
                    if (Objects.nonNull(etlSettleDate)) {
                        settleDate = DateUtils.formatDate(etlSettleDate);
                    }
                    // 还是空的话
                    if (Objects.isNull(etlSettleDate)) {
                        settleDate = DateUtils.formatDate(maxExpireDate);
                    }
                }
            }

        }
        return settleDate;
    }

    /**
     * 客户续场景景判断
     *
     * @param req
     * @return
     */
    @Override
    public CustomerRenewalRes customerSceneInfo(CustomerRenewalReq req) throws TxnBizException {
        CustomerRenewalRes res = new CustomerRenewalRes();
        BeanUtils.copyProperties(req, res);
        String userId = req.getUserId();
        String channel = req.getChannel();
        String tenantId = req.getTenantId();
        CmCustomerSecondLevel secondLevel = extCmCustomerSecondLevelMapper.selectByUserIdAndChannel(userId, channel, tenantId);
        if (Objects.isNull(secondLevel)) {
            logger.warn("二级客户信息未找到：{}", userId);
            return res;
        }
        String customerId = secondLevel.getCustomerId();
        CmCustomerFirstLevel firstLevel = extCmCustomerFirstLevelMapper.selectFirstLevelByOrgIdAndCusId(customerId, secondLevel.getOrganizationId(), tenantId);
        if (Objects.isNull(firstLevel)) {
            logger.warn("一级客户信息未找到：{}", customerId);
            return res;
        }
        // 没有封锁码表示正常
        if (StringUtils.isNotBlank(firstLevel.getBlockCode())) {
            // 当天是否逾期      1.是、0.否
            res.setTodayOverdueFlag(Constant.YES_FLAG);
        } else {
            PrOrganizationTable org = organizationTableService.findByOrId(CustomerConstant.DEFAULT_ORG_ID, Constants.DEFAULT_TENANT_ID);
            Date businessDate = org.getNextProcessingDate();
            res.setBusinessDate(DateUtils.format(businessDate));
            String settleDate = this.calculateSettleDate(tenantId, customerId, req.getProductId(), org.getOrganizationId(), businessDate);
            res.setPayoffDate(settleDate);
        }
        // 获取客户额度和综合额度有效期
        CmLimitChangeLog cmLimitChangeLog = extCmLimitChangeLogMapper.selectLimitOneByProductIdAndCustomerId(tenantId, channel, req.getProductId(), customerId);
        if (Objects.isNull(cmLimitChangeLog)) {
            return res;
        }
        res.setExpiryDate(cmLimitChangeLog.getExpiryDate());
        res.setEtlExpiryDate(cmLimitChangeLog.getEtlExpiryDate());
        return res;
    }

    /**
     * 获取用戶最新的综合额度生效日期
     *
     * @param tenantId
     * @param customerId
     * @param productIds
     * @return
     */
    @Override
    public CmLimitChangeLog queryLatestEtlEffectDate(String tenantId, String customerId, List<String> productIds) {
        return extCmLimitChangeLogMapper.queryLatestEtlEffectDate(tenantId, customerId, productIds);
    }

    /**
     * 获取客户某产品的额度变更记录
     * @param tenantId
     * @param customerId
     * @param productId
     * @return
     */
    @Override
    public List<CmLimitChangeLog> queryChangeLogs(String tenantId, String customerId, String productId) {
        return extCmLimitChangeLogMapper.queryChangeLogsByCustomerIdAndProductId(tenantId, customerId, productId);
    }

    /**
     * 激活时更新授信合同号和额度编号
     *
     * @param req
     */
    @Override
    public void updateEtlInfos(CreditActiveReq req) throws Exception {
        List<CreditInfo> creditInfoList = req.getCreditInfoList();
        if (CollectionUtils.isNotEmpty(creditInfoList)) {
            String tenantId = req.getTenantId();
            String userId = req.getUserId();
            String channel = req.getChannel();
            CmCustomerSecondLevel secondLevel = extCmCustomerSecondLevelMapper.selectByUserIdAndChannel(userId, channel, tenantId);
            String customerId = secondLevel.getCustomerId();
            CreditInfo creditInfo = creditInfoList.get(0);
            String productId = creditInfo.getProductId();
            String contractNumber = creditInfo.getContractNumber();
            // 通过产品编号判断同系列有哪些产品编号
            List<String> seriesProducts = BizTypeProducts.getSeriesProductIds(productId);
            PrOrganizationTable org = organizationTableService.findByOrId(CustomerConstant.DEFAULT_ORG_ID, tenantId);

            // 校验产品激活情况
            List<String> xahSeriesProducts = BizTypeProducts.getSeriesProductIds(productId);
            List<String> activeProducts = this.getActiveProducts(customerId, tenantId, xahSeriesProducts);
            // 已经有改过了了不需要再次修改
            if (CollectionUtils.isNotEmpty(activeProducts) && activeProducts.size() > 1) {
                logger.warn("已经有其他产品{}激活过了，无需激活", activeProducts);
                return;
            }
            //用户是否已经有对应的综合额度信息
            CmLimitChangeLog changeLog = extCmLimitChangeLogMapper.queryFirstLimitChangeLog(tenantId, customerId, seriesProducts);
            Date nextProcessingDate = org.getNextProcessingDate();
            Date etlExpiryDate = changeLog.getEtlExpiryDate();
            String etlContractNumber = changeLog.getEtlContractNumber();
            // 只能修改还未到期的
            // 跟现有的不一样，则需要修改
            logger.warn("用户{}已经过期了不能修改,当前业务日期{}，到期日期{}", customerId, nextProcessingDate, etlExpiryDate);
            if (nextProcessingDate.compareTo(etlExpiryDate) <= 0 && !contractNumber.equalsIgnoreCase(etlContractNumber)) {
                CmCustomerLimitInfo cond = new CmCustomerLimitInfo();
                cond.setTenantId(tenantId);
                cond.setCustomerId(customerId);
                cond.setContractNumber(contractNumber);
                cond.setChannel(channel);
                List<CmCustomerLimitInfo> ls = extCmCustomerLimitInfoMapper.selectByCondition(cond);
                List<CmLimitChangeLog> cmLimitChangeLogs = extCmLimitChangeLogMapper.queryAllByEtlContractNumber(tenantId, etlContractNumber, customerId);
                Optional<String> limit = ls.stream().max(Comparator.comparing(CmCustomerLimitInfo::getLimitExpiryDate)).map(CmCustomerLimitInfo::getLimitId);
                if (limit.isPresent()) {
                    String limitId = limit.get();
                    logger.info("limitId:{}", limitId);
                    cmLimitChangeLogs.forEach(log -> {
                        CmLimitChangeLog updateLog = new CmLimitChangeLog();
                        updateLog.setEtlContractNumber(contractNumber);
                        updateLog.setEtlLimitId(limitId);
                        updateLog.setId(log.getId());
                        updateLog.setCustomerId(customerId);
                        updateLog.setTenantId(tenantId);
                        extCmLimitChangeLogMapper.updateEtlContractNumberAndEtlLimitId(updateLog);
                    });
                }
            }
        }
    }

    /**
     * 查询激活的产品ids
     *
     * @param customerId
     * @param tenantId
     * @param productIds
     * @return
     * @throws Exception
     */
    @Override
    public List<String> getActiveProducts(String customerId, String tenantId, List<String> productIds) throws Exception {
        ProductActiveReq productActiveReq = new ProductActiveReq();
        productActiveReq.setChannelNo(Constants.DEFAULT_CHANNEL);
        CmCustomerSecondLevel secondLevel = iCustomerService.getSecondByChannelAndCustomerIdAndTensnId(Constants.DEFAULT_CHANNEL, customerId, tenantId, null);
        productActiveReq.setUserId(secondLevel.getUserId());
        productActiveReq.setTenantId(tenantId);
        ApiResponse<List<ProductActiveDto>> apiResponse = null;
        try {
            apiResponse = iUserApiService.activeInfoQuery(JsonUtils.toJSon(productActiveReq));
        } catch (Exception e) {
            e.printStackTrace();
            logger.warn("查询产品激活信息失败", e);

        }
        if (apiResponse == null || TxnRespCode.SUCCESS.getCode() != apiResponse.getCode()) {
            logger.warn("查询产品激活信息失败");
            throw new TxnBizException(9999, "客户未激活产品");
        }
        List<ProductActiveDto> productActiveDtoList = apiResponse.getData();
        if (CollectionUtils.isEmpty(productActiveDtoList)) {
            return null;
        }
        List<String> activeProducts = productActiveDtoList.stream()
                .filter(pro -> Constant.YES_FLAG.equalsIgnoreCase(pro.getActiveFlag()))
                .filter(pro -> productIds.contains(pro.getProductNumber()))
                .map(ProductActiveDto::getProductNumber)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(activeProducts)) {
            return null;
        }
        return activeProducts;
    }


}
